#include "RenzoPrecompiled.h"
#include "RenzoMeshMd2.h"
#include "RenzoVertex.h"
#include "RenzoVertexBuffer.h"
#include "RenzoRenderSystem.h"
#include "RenzoImageManager.h"
#include "RenzoTextureManager.h"
#include "RenzoResourceManager.h"
#include "RenzoSceneNode.h"
#include "RenzoAnimatorKeyFrame.h"

namespace Renzo
{

void MeshMd2::init() {
	ext = "md2";

	header = 0;
	textureUV = 0;
	faces = 0;
	frameHeader = 0;
	frames = 0;
	

//	triangleType = TRIANGLE_LIST;
}

MeshMd2::MeshMd2(void)
{
	init();	
}


MeshMd2::~MeshMd2(void)
{
	Cleanup();
}

void MeshMd2::update(int time) {
	
}



void MeshMd2::Cleanup() {
	rzDel(header);
	rzDels(textureUV);
	rzDels(faces);
	rzDels(frames);
	rzDels(frameHeader);
	
	//rzDels(fVertices);
	//rzDels(fTextures);	
}

/*
BoundingBox* MeshMd2::getBoundingBoxAABB() {
	// TODO: should we compute the bounding box for every frame, or for all frames?
	
	int offFrame = curFrame * header->numVertices;

	bb.min = INF_MAX;
	bb.max = INF_MIN;
	for (int i = 0; i < header->numVertices; ++i) {
		bb.min = rzMin3(bb.min, vertices[offFrame + i].position);
		bb.max = rzMax3(bb.max, vertices[offFrame + i].position);
	}

	bb.center = (bb.min + bb.max) * 0.5f;
	bb.dim = bb.max - bb.min;
	bb.radius = bb.dim.length() * 0.5f;

	return &bb;
	
}
*/

SceneNode* MeshMd2::load(const String& file) {
	FILE* f;
	//char fileStr[1024];
	//strcpy(fileStr, file.c_str());
	int i, j, k;
	f = fopen(file.c_str(), "rb"); // !!! must add b to read in binary mode!!! If not, read in some correct data then wrong!!! Ahhh!!!
	//f = fopen("E:\\task.txt", "r");
	if (f == 0) {
		rzSevere(EXIT_FILE_NOT_FOUND, ERROR_FILE_NOT_FOUND, file);		
	}

	/* header */
	header = new Md2Header;
	//fread(header, sizeof(int), 17, f);
	fread(header, sizeof(Md2Header), 1, f);
	/* allocate space for vertices and textureUV */
	//textureUV = new Md2TextureCoordinate[header->numVertices];
	textureUV = new Md2TextureCoordinate[header->numTexCoords];
	faces = new Md2Face[header->numFaces];
	frameHeader = new Md2FrameHeader[header->numFrames]; //each frame has a frame header
	frames = new Md2Frame[header->numFrames];
	for (i = 0; i < header->numFrames; i++)
		frames[i].vertices = new Md2Vertex[header->numVertices];
	
	/* texture name, 64 bytes, not used */
	char textureName[65];
	memset(textureName, 0, 65);
	fseek(f, header->offTextures, SEEK_SET);
	fread(textureName, sizeof(char), 64, f);

	/* texture coordinate */
	fseek(f, header->offTexCoords, SEEK_SET);
	fread(textureUV, sizeof(Md2TextureCoordinate), header->numTexCoords, f);
	
	/* faces */
	fseek(f, header->offFace, SEEK_SET);
	fread(faces, sizeof(Md2Face), header->numFaces, f);
	
	/* frames */
	fseek(f, header->offFrame, SEEK_SET);
	for (i = 0; i < header->numFrames; i++) {
		/* frames header */
		fread(&frameHeader[i], sizeof(Md2FrameHeader), 1, f);
		/* frames data */
		fread(frames[i].vertices, sizeof(Md2Vertex), header->numVertices, f);
	}
	/* decompress the whole vertices and texture coordinates */
	/* vertices */
	//fVertices = new Vector3f[header->numFrames*header->numVertices];
	//k = 0;	

	ResourceManager* rm = Renzoderer::instance()->getResourceManager();
	PolygonGroup* group = (PolygonGroup*)rm->createObject(PolygonGroupType);

	/*
	// enforce that the same vertex index must have the same texture coordinate
	for (i = 0; i < header->numFaces; ++i) {
		
	}

	Vertex v; 
	for (i = 0; i < header->numFrames; ++i) {
		for (j = 0; j < header->numVertices; ++j) {
			v.position = Float3(
				frames[i].vertices[j].compressedVertex[0] * frameHeader[i].scale[0] + frameHeader[i].translate[0],
				frames[i].vertices[j].compressedVertex[1] * frameHeader[i].scale[1] + frameHeader[i].translate[1],
				frames[i].vertices[j].compressedVertex[2] * frameHeader[i].scale[2] + frameHeader[i].translate[2]
			);
		}
	}*/

	// create 3 vertices per face
	Vertex v; 
	Face face;
	for (i = 0; i < header->numFrames; ++i) {
		for (j = 0; j < header->numFaces; ++j) {
			for (int k = 0; k < 3; ++k) {
				int p = faces[j].indexVertex[k];
				v.position = Float3(
					frames[i].vertices[p].compressedVertex[0] * frameHeader[i].scale[0] + frameHeader[i].translate[0],
					frames[i].vertices[p].compressedVertex[1] * frameHeader[i].scale[1] + frameHeader[i].translate[1],
					frames[i].vertices[p].compressedVertex[2] * frameHeader[i].scale[2] + frameHeader[i].translate[2]
				);

				int q = faces[j].indexTexture[k];
				v.texCoords[0] = Float4(
					(float) textureUV[q].compressedU / header->texWidth,
					(float) textureUV[q].compressedV / header->texHeight,
					0, 0);

				//vertices.push_back(v);
				group->addVertex(&v);
			}
			int idx = i * header->numFaces + j;
			face.indices[0] = 3 * idx;
			face.indices[2] = 3 * idx + 1;
			face.indices[1] = 3 * idx + 2;
			group->addFace(&face);
		}
	}
	/*
	Vertex v; 
	for (i = 0; i < header->numFrames; i++) {
		for (j = 0; j < header->numVertices; j++) {
			// !!! Must redeclare vertex v every iteration as v.texCoords is different for every vertex. If declare outside, v.texCoords does not change, thus destroy all texcoords data.
			

			v.position = Float3(
				frames[i].vertices[j].compressedVertex[0] * frameHeader[i].scale[0] + frameHeader[i].translate[0],
				frames[i].vertices[j].compressedVertex[1] * frameHeader[i].scale[1] + frameHeader[i].translate[1],
				frames[i].vertices[j].compressedVertex[2] * frameHeader[i].scale[2] + frameHeader[i].translate[2]
			);

			//printf("%f %f %f\n", v.position.x, v.position.y, v.position.z);
			//printf("%d %d %d\n", frames[i].vertices[j].compressedVertex[0],  frames[i].vertices[j].compressedVertex[1], frames[i].vertices[j].compressedVertex[2]);
			//printf("%d %d\n", textureUV[j].compressedU, textureUV[j].compressedV);
			v.texCoords[0] = Float4(
				(float) textureUV[j].compressedU / header->texWidth,
				(float) textureUV[j].compressedV / header->texHeight,
				0, 0);

			vertices.push_back(v);

			
		}
	}*/
	
	/* textures */
	// avoid slashes in texture name
	char* pName = textureName + 64;
	while (pName >= textureName && *pName != '/' && *pName != '\\') --pName;
	String filename;
	filename.append("data/texture/");
	filename.append(pName + 1);
	Image* image = Renzoderer::instance()->getImageManager()->load(filename);
	Texture* tex = Renzoderer::instance()->getTextureManager()->createTexture(image);
	//textures.push_back(tex);
	group->setTexture(tex);

	/*
	fTextures = new Vector3f[header->numFrames*header->numVertices];
	for (i = 0; i < header->numVertices; i++)
		fTextures[i] = Vector3f(
			(float) textureUV[i].compressedU / header->texWidth,
			(float) textureUV[i].compressedV / header->texHeight,
			0
		);
		*/
		
	/* output something */
	printf("Vertices: %d\n",	header->numVertices);
	printf("TexCoords: %d\n", header->numTexCoords);
	printf("Frames: %d\n",		header->numFrames);
	printf("Frame size: %d\n",	header->frameSize);
	printf("Texture name: %s\n",	textureName);
	printf("Faces: %d\n", header->numFaces);
	printf("offTextures: %d\n",		header->offTextures);
	printf("offTexCoords: %d\n",	header->offTexCoords);	// offset to texture coordinates
	printf("offFace: %d\n",		header->offFace);			// offset to triangle list
	printf("offFrame: %d\n",	header->offFrame);
	//printf("Vertices[0]: %s\n", fVertices[0].ToString());
	
	fclose(f);

	// return a new scene node
	AnimatorKeyFrame* animator = new AnimatorKeyFrame;
	animator->initialize(0, header->numFrames, header->numFaces, header->numVertices);
	group->setAnimator(animator);

	Mesh* mesh = (Mesh*)rm->createObject(MeshType);
	mesh->addPolygonGroup(group);
	SceneNode* node = new SceneNode;
	node->attachObject(mesh);
	return node;
}

}
